﻿using System;
using System.IO;
using System.Windows;
using System.Xml;

namespace SHomeWorkshop.LunarConcept.Tools
{
    /// <summary>
    /// [静态]Xml功能类。
    /// 创建时间：2011年12月26日
    /// 创建者：杨震宇
    /// 
    /// 主要内容：为XmlNode添加了几个扩展方法，以便操作Xml文件。
    /// </summary>
    public static class XmlTools
    {
        /// <summary>
        /// [扩展方法]将一串Xml格式的文本转换成XmlNode节点，并插入为此节点的子节点。
        /// 
        /// 异常：
        /// 　　ArgumentNullException
        /// </summary>
        /// <param name="xmlNode">调用本方法的Xml节点（本节点将成为新节点的父节点）。</param>
        /// <param name="xmlText">需要转换成新子节点的Xml文本（需要符合Xml格式）。</param>
        /// <returns>如果成功转换并插入到当前节点尾部，成为当前节点的最后一个子节点，就返回【新子节点】。失败则返回null。</returns>
        public static XmlNode AppendXmlAsChild(this XmlNode xmlNode, String xmlText)
        {

            if (xmlText == null || xmlText.Length <= 0)
                throw new ArgumentNullException("XmlNode的AppendXmlAsChild()扩展方法的“xmlText”参数值不能为null或空字符串！");
            try
            {

                XmlDocument doc = new XmlDocument();
                doc.LoadXml(xmlText);
                XmlNode newNode = xmlNode.OwnerDocument.CreateNode
                    (doc.FirstChild.NodeType, doc.FirstChild.Name, doc.FirstChild.NamespaceURI);

                foreach (XmlAttribute attribute in doc.FirstChild.Attributes)
                {
                    XmlAttribute newAttribute = xmlNode.OwnerDocument.CreateAttribute(attribute.Name);
                    newAttribute.Value = attribute.Value;
                    newNode.Attributes.Append(newAttribute);
                }

                newNode.InnerXml = doc.FirstChild.InnerXml;

                xmlNode.AppendChild(newNode);

                return newNode;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return null;
            }
        }

        public static XmlElement AppendXmlAsChild(this XmlElement xmlElement, String xmlText)
        {

            if (xmlText == null || xmlText.Length <= 0)
                throw new ArgumentNullException("XmlNode的AppendXmlAsChild()扩展方法的“xmlText”参数值不能为null或空字符串！");

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xmlText);
            XmlElement newElement = xmlElement.OwnerDocument.CreateElement(doc.FirstChild.Name, doc.FirstChild.NamespaceURI);

            foreach (XmlAttribute attribute in doc.FirstChild.Attributes)
            {
                XmlAttribute newAttribute = xmlElement.OwnerDocument.CreateAttribute(attribute.Name);
                newAttribute.Value = attribute.Value;
                newElement.Attributes.Append(newAttribute);
            }

            newElement.InnerXml = doc.FirstChild.InnerXml;

            xmlElement.AppendChild(newElement);

            return newElement;
        }

        /// <summary>
        /// [扩展方法]取出当前节点XmlNode中指定名称的属性。如果指定的属性不存在，会返回null。
        /// </summary>
        /// <param name="xmlNode">要取属性的节点。</param>
        /// <param name="attributeName">要取的属性的名称。</param>
        /// <returns>返回指定名称的属性。如果不存在，返回null。</returns>
        public static XmlAttribute GetAttribute(this XmlNode xmlNode, String attributeName)
        {
            if (xmlNode == null) return null;
            if (xmlNode.Attributes == null) return null;

            XmlAttributeCollection attributes = xmlNode.Attributes;
            foreach (XmlAttribute attribute in attributes)
            {
                if (attribute.Name == attributeName)
                {
                    return attribute;
                }
            }

            return null;
        }

        /// <summary>
        /// [扩展方法]取XmlElement的属性。如果存在指定名称的属性（Attribute），
        /// 返回此属性（Attribute）。否则返回null。
        /// </summary>
        /// <param name="xmlElement">要调用本扩展方法的XmlElement元素自身。</param>
        /// <param name="attributeName">要删除的属性的名称。</param>
        /// <returns>返回属性。</returns>
        public static XmlAttribute GetAttributeByName(this XmlElement xmlElement, String attributeName)
        {
            if (xmlElement.Attributes == null) return null;

            XmlAttributeCollection attributes = xmlElement.Attributes;
            foreach (XmlAttribute attribute in attributes)
            {
                if (attribute.Name == attributeName)
                {
                    return attribute;
                }
            }

            return null;
        }

        /// <summary>
        /// [扩展方法]取出节点中指定名称的属性的文本值。如果不存在，返回null，而不是返回空字符串。
        /// </summary>
        /// <param name="xmlNode">调用本方法的XmlNode节点（取其属性值的节点）。</param>
        /// <param name="attributeName">要取值的属性的名称。</param>
        /// <returns>返回属性的值（文本值）。如果不存在该属性，返回null。</returns>
        public static String GetAttributeValueText(this XmlNode xmlNode, String attributeName)
        {
            XmlAttributeCollection attributes = xmlNode.Attributes;
            foreach (XmlAttribute attribute in attributes)
            {
                if (attribute.Name == attributeName)
                {
                    return attribute.InnerText;
                }
            }

            return null;
        }

        /// <summary>
        /// [扩展方法]取本节点的子节点列表中指定的某个子节点的索引值。
        /// 如果指定的节点不是当前节点的子节点，返回-1。
        /// 
        /// 异常：
        /// 　　Exception
        /// </summary>
        /// <param name="xmlNode">调用本方法的节点。</param>
        /// <param name="childNode">应为调用节点的子节点。返回值将是这个子节点的索引。</param>
        /// <returns>如果指定子节点不是调用此方法的节点的子节点，返回-1。如果找到，则返回其索引。</returns>
        public static int IndexOf(this XmlNode xmlNode, XmlNode childNode)
        {
            if (childNode == null) return -1;

            XmlNode parentNode = xmlNode;
            if (parentNode == null)
                throw new Exception("XmlNode的IndexOf()扩展方法在执行时未找到父节点。");

            int curIndex = -1;
            for (int i = 0; i < parentNode.ChildNodes.Count; i++)
            {
                XmlNode node = parentNode.ChildNodes[i];
                if (node == childNode)
                {
                    curIndex = i; break;
                }
            }

            return curIndex;
        }

        /// <summary>
        /// [扩展方法]在当前节点的子节点列表中，按指定索引插入一个新的XmlNode。
        /// 如果插入成功，返回true。
        /// 
        /// 异常：
        /// 　　IndexOutOfRangeException
        /// </summary>
        /// <param name="xmlNode">调用本方法的节点（新节点将成为此节点的子节点）。</param>
        /// <param name="index">新节点应插入到的位置。</param>
        /// <param name="newXmlChildNode">新子节点。</param>
        /// <returns>如果插入成功，返回true。</returns>
        public static bool InsertChildNodeAt(this XmlNode xmlNode, int index, XmlNode newXmlChildNode)
        {
            if (xmlNode.OwnerDocument == null)
                throw new Exception("节点不在文档中！");

            if (xmlNode.OwnerDocument.PreserveWhitespace)
                throw new Exception("Xml文档的“PreserveWhitespace”属性值不应为true。");

            if (index < 0)
                throw new IndexOutOfRangeException("XmlNode的InsertChildNodeAt()扩展方法的“index”参数值不能小于0！");

            if (index == 0)
            {
                if (xmlNode.FirstChild != null)
                {
                    xmlNode.InsertBefore(newXmlChildNode, xmlNode.FirstChild);
                    return true;
                }
                else
                {
                    xmlNode.AppendChild(newXmlChildNode);
                    return true;
                }
            }

            if (index == xmlNode.ChildNodes.Count)
            {
                xmlNode.AppendChild(newXmlChildNode);
                return true;
            }

            XmlNodeList childNodeList = xmlNode.ChildNodes;
            if (index > childNodeList.Count)
                throw new IndexOutOfRangeException("XmlNode的InsertChildNodeAt()扩展方法的“index”参数值不能大于子节点总数目！");

            return (xmlNode.InsertAfter(newXmlChildNode, childNodeList[index - 1]) != null);
        }

        /// <summary>
        /// [扩展方法]将提供的Xml文本转换成一个XmlNode并插入到自身的后面或前面。
        /// 新节点与当前调用本方法的节点平级。
        /// 注意：只能传入一个节点的OutXmlText，不能同时传入多个节点的。
        /// </summary>
        /// <param name="xmlNode">调用本方法的节点。（新节点将与本节点平级。）</param>
        /// <param name="xmlText">新节点的OutXmlText文本。</param>
        /// <param name="afterMe">默认为：true，表示在本节点之后；false表示在本节点之前插入。</param>
        /// <returns>如果插入成功，返回true。</returns>
        public static bool InsertXml(this XmlNode xmlNode, String xmlText, bool afterMe = true)
        {
            try
            {
                XmlNode parentNode = xmlNode.ParentNode;
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(xmlText);

                XmlNode newNode = xmlNode.OwnerDocument.CreateNode
                    (doc.FirstChild.NodeType, doc.FirstChild.Name, doc.FirstChild.NamespaceURI);

                foreach (XmlAttribute attribute in doc.FirstChild.Attributes)
                {
                    XmlAttribute newAttribute = xmlNode.OwnerDocument.CreateAttribute(attribute.Name);
                    newAttribute.Value = attribute.Value;
                    newNode.Attributes.Append(newAttribute);
                }

                newNode.InnerXml = doc.FirstChild.InnerXml;

                if (afterMe)
                {
                    parentNode.InsertAfter(newNode, xmlNode);
                }
                else
                {
                    parentNode.InsertBefore(newNode, xmlNode);
                }

                return true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return false;
            }
        }

        /// <summary>
        /// [扩展方法]在指定索引处插入新的Xml节点。新节点将成为子节点。返回子节点。
        /// 
        /// 异常：
        /// 　　IndexOutOfRangeException
        /// 　　ArgumentNullException
        /// </summary>
        /// <param name="xmlNode">调用本方法的XmlNode（将成为新节点的父节点）。</param>
        /// <param name="xmlText">新子节点的OutXml文本。</param>
        /// <param name="index">新子节点应出现在父节点中哪个位置，索引值从0开始计算。</param>
        /// <returns>返回子节点。</returns>
        public static XmlNode InsertXmlAt(this XmlNode xmlNode, String xmlText, int index)
        {
            if (index < 0)
                throw new IndexOutOfRangeException("XmlNode的InsertXmlAt()扩展方法的“index”参数值不能小于0！");

            if (xmlText == null || xmlText.Length <= 0)
                throw new ArgumentNullException("XmlNode的InsertXmlAt()扩展方法的“xmlText”参数不能为null或空字符串！");

            if (index > xmlNode.ChildNodes.Count)
                throw new IndexOutOfRangeException("XmlNode的InsertXmlAt()扩展方法的“index”参数值不能大于子节点最大索引！");

            try
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(xmlText);

                XmlNode newNode = xmlNode.OwnerDocument.CreateNode
                    (doc.FirstChild.NodeType, doc.FirstChild.Name, doc.FirstChild.NamespaceURI);

                foreach (XmlAttribute attribute in doc.FirstChild.Attributes)
                {
                    XmlAttribute newAttribute = xmlNode.OwnerDocument.CreateAttribute(attribute.Name);
                    newAttribute.Value = attribute.Value;
                    newNode.Attributes.Append(newAttribute);
                }

                newNode.InnerXml = doc.FirstChild.InnerXml;

                if (index > 0)
                {
                    XmlNode preNode = xmlNode.ChildNodes[index - 1];
                    if (preNode == null) return null;

                    return xmlNode.InsertAfter(newNode, preNode);
                }
                else
                {
                    return xmlNode.InsertBefore(newNode, xmlNode.FirstChild);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return null;
            }
        }

        /// <summary>
        /// [静态方法]从磁盘上读取Xml文件，如果正常，返回一个XmlDocument对象。
        /// 
        /// 异常：
        /// 　　ArgumentNullException
        /// 　　FileNotFoundException
        /// </summary>
        /// <param name="fileFullPath">Xml文件在磁盘上的完整路径。</param>
        /// <returns>如果正常，返回一个XmlDocument对象。</returns>
        public static XmlDocument LoadXmlFile(string fileFullPath)
        {
            if (fileFullPath == null || fileFullPath.Length <= 0)
                throw new ArgumentNullException("XmlTools.LocaXmlFile()方法的“fileFullPath”参数值不能为null或空字符串！");

            if (System.IO.File.Exists(fileFullPath) == false)
                throw new FileNotFoundException("XmlTools.LocaXmlFile()方法未能找到文件：" + fileFullPath);

            XmlReader reader = null;

            try
            {
                XmlReaderSettings settings = new XmlReaderSettings();
                settings.ConformanceLevel = ConformanceLevel.Document;
                settings.IgnoreWhitespace = true;
                settings.IgnoreComments = true;

                string uriStr = fileFullPath; //fileFullPath.StartsWith("file:///") ? fileFullPath : "file:///" + fileFullPath;

                reader = XmlReader.Create(uriStr, settings);

                XmlDocument xmlDoc = new XmlDocument();
                xmlDoc.RemoveAll();

                System.Xml.XmlNode node = xmlDoc.ReadNode(reader); ;

                while (node != null)
                {
                    xmlDoc.AppendChild(node);
                    node = xmlDoc.ReadNode(reader);
                }

                return xmlDoc;
            }
            finally
            {
                if (reader != null) reader.Close();
            }
        }

        /// <summary>
        /// [扩展方法]将当前节点移动到同级节点中的指定索引位置。如果当前位置即是指定位置，也返回true。
        /// 如果此节点没有父节点，则不能移动。如果指定索引值越界，也不能移动。
        /// 
        /// 异常：
        /// 　　Exception
        /// 　　IndexOutOfRangeException
        /// </summary>
        /// <param name="xmlNode">调用本方法的当前节点。</param>
        /// <param name="newIndex">当前节点的新索引。必须介于[0，同级节点数目-1]之间。</param>
        /// <returns>如果成功返回true。</returns>
        public static bool MoveTo(this XmlNode xmlNode, int newIndex)
        {
            if (newIndex < 0)
                throw new IndexOutOfRangeException("XmlNode的扩展方法MoveTo()的“newIndex”参数值不能小于零！");

            XmlNode parentNode = xmlNode.ParentNode;
            if (parentNode == null)
                throw new Exception("XmlNode的扩展方法MoveTo()在执行时未找到“parentNode”，无法继续。");

            if (newIndex >= parentNode.ChildNodes.Count)
                throw new IndexOutOfRangeException("XmlNode的扩展方法MoveTo()的“newIndex”参数值不能大于同级节点最大索引！");

            int curIndex = parentNode.IndexOf(xmlNode);

            if (curIndex < 0)
                throw new Exception("发生意外，未找到当前节点在同级节点列表中的索引。XmlNode的IndexOf()扩展方法执行错误。");

            if (newIndex < curIndex)
            {
                parentNode.RemoveChild(xmlNode);
                parentNode.InsertBefore(xmlNode, parentNode.ChildNodes[newIndex]);
                return true;
            }
            else if (newIndex > curIndex)
            {
                XmlNode baseDestNode = parentNode.ChildNodes[newIndex];
                if (baseDestNode == null)
                    throw new IndexOutOfRangeException("XmlNode的扩展方法MoveTo()执行“向后移动”时未能找到“newIndex”位置的节点！");

                parentNode.RemoveChild(xmlNode);
                parentNode.InsertAfter(xmlNode, baseDestNode);
                return true;
            }
            else return true;
        }

        /// <summary>
        /// [扩展方法]将调用本方法的节点与同级节点中的前一个节点对调位置。
        /// 如果此节点本身已经是同级节点中的首位，则不进行任何操作并返回false。
        /// 
        /// 异常：
        /// 　　Exception
        /// </summary>
        /// <param name="xmlNode">调用本方法的节点本身。</param>
        /// <returns>如果找不到父节点，或本身就是首节点，返回false。成功后返回true。</returns>
        public static bool MoveToPrevious(this XmlNode xmlNode)
        {
            XmlNode previousNode = xmlNode.PreviousSibling;
            if (previousNode == null) return false;

            XmlNode parentNode = xmlNode.ParentNode;
            if (parentNode == null)
                throw new Exception("XmlNode的MoveToPrevious()扩展方法在执行时未找到父节点。");

            parentNode.RemoveChild(xmlNode);
            parentNode.InsertBefore(xmlNode, previousNode);
            return true;
        }

        /// <summary>
        /// [扩展方法]将调用本方法的节点与同级节点中的后一个节点对调位置。
        /// 如果此节点本身已经是同级节点中的最后一个节点，则不进行任何操作并返回false。
        /// 
        /// 异常：
        /// 　　Exception
        /// </summary>
        /// <param name="xmlNode">调用本方法的节点本身。</param>
        /// <returns>如果找不到父节点，或本身就是最后一个节点，返回false。成功后返回true。</returns>
        public static bool MoveToNext(this XmlNode xmlNode)
        {
            XmlNode nextNode = xmlNode.NextSibling;
            if (nextNode == null) return false;

            XmlNode parentNode = xmlNode.ParentNode;
            if (parentNode == null)
                throw new Exception("XmlNode的MoveToNext()扩展方法在执行时未找到父节点。");

            parentNode.RemoveChild(xmlNode);
            parentNode.InsertAfter(xmlNode, nextNode);
            return true;
        }

        /// <summary>
        /// [扩展方法]删除此节点的所有子节点（node）。但不删除Attributes。
        /// </summary>
        /// <param name="xmlElement">调用此扩展方法的XmlNode实例本身。</param>
        public static void RemoveAllChildNodes(this XmlNode xmlNode)
        {
            if (xmlNode == null) return;
            if (xmlNode.ChildNodes.Count <= 0) return;

            for (int i = xmlNode.ChildNodes.Count - 1; i >= 0; i--)
            {
                xmlNode.RemoveChild(xmlNode.ChildNodes[i]);
            }
        }

        /// <summary>
        /// [扩展方法]移除指定名称的属性。
        /// </summary>
        /// <param name="xmlNode">节点。</param>
        /// <param name="attributeName">属性名。</param>
        /// <returns>是否移除成功。</returns>
        public static bool RemoveAttributeByName(this XmlNode xmlNode, string attributeName)
        {
            if (xmlNode == null) return false;
            if (xmlNode.Attributes == null) return false;

            XmlAttributeCollection attributes = xmlNode.Attributes;
            foreach (XmlAttribute attribute in attributes)
            {
                if (attribute.Name == attributeName)
                {
                    xmlNode.Attributes.Remove(attribute);
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// [扩展方法]替换当前节点的OutXmlText（此节点的所有内容：名称、特性、子节点等等均将由提供的Xml字符串所决定。
        /// 返回替换后的新节点。
        /// 
        /// 异常：
        /// 　　ArgumentNullException
        /// </summary>
        /// <param name="xmlNode">调用本方法的（要替换的）节点。</param>
        /// <param name="newOutXmlText">节点的新OutXml文本。</param>
        /// <returns>返回被替换后的新节点。</returns>
        public static XmlNode ReplaceOutXmlText(this XmlNode xmlNode, string xmlText)
        {
            if (xmlText == null || xmlText.Length <= 0)
                throw new ArgumentNullException("XmlNode的ReplaceOutXmlText()扩展方法的“XmlText”参数不能为null或空字符串！");

            try
            {
                XmlNode parentNode = xmlNode.ParentNode;
                XmlDocument doc = new XmlDocument();

                //Load方法不需要XmlHeader！！！
                //if (xmlText.StartsWith("<xml") == false)
                //{
                //    xmlText = xmlHeaderText + xmlText;
                //}

                doc.LoadXml(xmlText);

                XmlNode newNode = xmlNode.OwnerDocument.CreateNode
                    (doc.FirstChild.NodeType, doc.FirstChild.Name, doc.FirstChild.NamespaceURI);

                if (doc.FirstChild.Attributes != null && doc.FirstChild.Attributes.Count > 0)
                {
                    foreach (XmlAttribute attribute in doc.FirstChild.Attributes)
                    {
                        XmlAttribute newAttribute = xmlNode.OwnerDocument.CreateAttribute(attribute.Name);
                        newAttribute.Value = attribute.Value;
                        newNode.Attributes.Append(newAttribute);
                    }
                }

                newNode.InnerXml = doc.FirstChild.InnerXml;

                if (parentNode.ReplaceChild(newNode, xmlNode) != null)
                {
                    return newNode;
                }
                else return null;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return null;
            }
        }

        /// <summary>
        /// [静态方法]将文本中的小于号和&符转换成&lt;和&amp;以防止造成Xml格式失效。
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string ReplaceXmlChars(string source)
        {
            if (source == null) return null;
            if (source.Length <= 0) return string.Empty;

            //Xml预定的转义字符有五个，只有“<”和“&”是严格禁止使用的。
            string result = source.Replace("&", "&amp;");
            result = result.Replace("<", "&lt;");
            result = result.Replace(">", "&gt;");
            result = result.Replace("'", "&apos;");
            result = result.Replace("\"", "&quot;");
            return result;
        }

        /// <summary>
        /// [静态方法]将Xml文本中的&lt;和&amp;转换回小于号和&符。
        /// </summary>
        /// <param name="source"></param>
        /// <returns></returns>
        public static string RestoreXmlChars(string source)
        {
            if (source == null) return null;
            if (source.Length <= 0) return string.Empty;

            //Xml预定的转义字符有五个，只有“<”和“&”是严格禁止使用的。
            string result = source.Replace("&quot;", "\"");
            result = result.Replace("&apos;", "'");
            result = result.Replace("&gt;", ">");
            result = result.Replace("&lt;", "<");
            result = result.Replace("&amp;", "&");
            return result;
        }

        /// <summary>
        /// [静态方法]将Xml序列字符串保存为一个磁盘上的Xml文件。
        /// ★注意：XmlDocument.LoadXml()方法传入的文本不需要以Xml声明文本开头！！！不必重复检查！！！
        /// 
        /// 异常：
        /// 　　ArgumentException
        /// 　　ArgumentNullException
        /// </summary>
        /// <param name="fullPathOfXmlFile">文件路径。</param>
        /// <param name="xmlString">Xml字符串。</param>
        /// <returns>返回保存后的XmlDocument对象。</returns>
        public static XmlDocument SaveToXmlFile(string fullPathOfXmlFile, string xmlString)
        {
            if (fullPathOfXmlFile == null || fullPathOfXmlFile.Length <= 0)
                throw new ArgumentNullException("XmlNode的SaveToXmlFile()扩展方法的“fullPathOfXmlFile”参数值不能为null或空字符串！");

            if (xmlString == null || xmlString.Length <= 0)
                throw new ArgumentNullException("XmlNode的SaveToXmlFile()扩展方法的“xmlString”参数值不能为null或空字符串！");

            //if (xmlString.StartsWith("<?xml") == false)
            //{
            //    xmlString = XmlTools.XmlHeaderText + xmlString;
            //}
            //LoadXml方法不需要XmlHeader。
            try
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(xmlString);

                string directoryString;

                int indexOfLastSeparator = fullPathOfXmlFile.LastIndexOf("\\");
                if (indexOfLastSeparator == -1)
                {
                    throw new ArgumentException("XmlTools.SaveToXmlFile()方法的“fullPathOfXmlFile”参数有误，未找到目录分割符“\\”。");
                }

                directoryString = fullPathOfXmlFile.Substring(0, indexOfLastSeparator);
                if (Directory.Exists(directoryString) == false)
                {
                    Directory.CreateDirectory(directoryString);
                    if (Directory.Exists(directoryString) == false) return null;//未能创建目录。
                }

                doc.Save(fullPathOfXmlFile);
                return doc;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, Globals.AppName, MessageBoxButton.OK, MessageBoxImage.Warning);
                return null;
            }
        }

        /// <summary>
        /// [扩展方法]为节点设置一个属性（Attribute）值。如果已存在该属性，则更新其值；若尚无该属性，则添加一个新属性。
        /// 
        /// 异常：
        /// 　　ArgumentNullException
        /// </summary>
        /// <param name="xmlNode">调用本方法（要设置属性值）的节点。</param>
        /// <param name="attributeName">属性名。不能是null或空字符串。</param>
        /// <param name="attributeValue">属性值。不能为null，但可以是空字符串。</param>
        public static void SetAttribute(this XmlNode xmlNode, string attributeName, string attributeValue)
        {
            if (attributeName == null || attributeName.Length <= 0)
                throw new ArgumentNullException("XmlNode的SetAttribute()扩展方法的“attributeName”参数值不能是null或空字符串！");

            if (attributeValue == null)
                throw new ArgumentNullException("XmlNode的SetAttribute()扩展方法的“attributeName”参数值不能是null！");

            XmlAttributeCollection attributes = xmlNode.Attributes;
            XmlAttribute attribute = null;

            foreach (XmlAttribute a in attributes)
            {
                if (a.Name == attributeName)
                {
                    attribute = a;
                    break;
                }
            }

            if (attribute == null)
            {
                XmlAttribute newAttribute = xmlNode.OwnerDocument.CreateAttribute(attributeName);
                newAttribute.InnerText = attributeValue;
                xmlNode.Attributes.Append(newAttribute);
            }
            else
            {
                if (attribute.InnerText != attributeValue)
                {
                    attribute.InnerText = attributeValue;
                }
            }
        }

        private static readonly string xmlHeaderText = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";

        /// <summary>
        /// [只读]返回Xml文件头一行声明字符串："&lt;?xml version=\"1.0\" encoding=\"utf-8\"?&gt;";
        /// </summary>
        public static string XmlHeaderText { get { return xmlHeaderText; } }

        public static string GetTypeAttributeValue(this XmlNode node)
        {
            if (node == null) return string.Empty;
            var attrType = node.GetAttribute(XmlTags.TypeTag);
            if (attrType == null) return string.Empty;

            return attrType.Value;
        }

        public static string GetIdAttributeValue(this XmlNode node)
        {
            if (node == null) return string.Empty;
            var attrType = node.GetAttribute(XmlTags.IdTag);
            if (attrType == null) return string.Empty;

            return attrType.Value;
        }

        public static XmlAttribute GetStartMasterIDAttributeValue(this XmlNode node)
        {
            if (node == null) return null;

            var attrStartMasterID = node.GetAttribute(XmlTags.StartMasterIdTag);
            if (attrStartMasterID != null) return attrStartMasterID;

            return null;
        }

        public static XmlAttribute GetEndMasterIDAttribute(this XmlNode node)
        {
            if (node == null) return null;

            var attrEndMasterID = node.GetAttribute(XmlTags.EndMasterIdTag);
            if (attrEndMasterID != null) return attrEndMasterID;

            return null;
        }

        public static bool IsLinkLineWidgetNode(this XmlNode node)
        {
            if (node == null) return false;

            var attrStartMasterID = node.GetAttribute(XmlTags.StartMasterIdTag);
            var attrEndMasterID = node.GetAttribute(XmlTags.EndMasterIdTag);

            if (attrStartMasterID == null || string.IsNullOrEmpty(attrEndMasterID.Value)) return false;
            if (attrEndMasterID == null || string.IsNullOrEmpty(attrEndMasterID.Value)) return false;

            return true;
        }
    }
}
